package com.sanyou.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sanyou.service.CacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class CacheServiceImpl implements CacheService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    Logger logger = LoggerFactory.getLogger(CacheService.class);

    private final String DEFAULT_KEY_PREFIX = "SANYOU:";
    private final int EXPIRE_TIME = 1;
    private final TimeUnit EXPIRE_TIME_TYPE = TimeUnit.DAYS;

    /**
     * 数据缓存至redis
     * @param key 键名
     * @param value 存入的对象实例
     * @param <V> 存入对象的类型
     */
    @Override
    public <V> void set(String key, V value) {
        try {
            if (!StringUtils.isEmpty(key) && value != null) {
                redisTemplate
                        .opsForValue()
                        .set(key, JSON.toJSONString(value));
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
    }

    /**
     * 数据缓存至redis并设置过期时间
     * @param key
     * @param value
     * @param timeout 时长
     * @param unit 时间单位
     * @param <V>
     */
    @Override
    public <V> void set(String key, V value, long timeout, TimeUnit unit) {
        try {
            if (!StringUtils.isEmpty(key) && value != null) {
                redisTemplate
                        .opsForValue()
                        .set(key, JSON.toJSONString(value), timeout, unit);
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
    }

    /**
     * 根据key从缓存数据库中获取对应值并转化为相应对象
     * @param key
     * @param clazz
     * @param <V>
     */
    @Override
    public <V> V get(String key, Class<V> clazz) {
        return JSONObject.parseObject(this.get(key), clazz);
    }

    /**
     * 根据key从缓存数据库中获取对应值
     * @param key
     * @return
     */
    @Override
    public String get(String key) {
        String value = null;
        try {
            if (!StringUtils.isEmpty(key)) {
                value = redisTemplate.opsForValue().get(key);
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
        return value;
    }

    /**
     * 数据右插到缓存数据库key对应的列表中
     * @param key
     * @param value
     * @param <V>
     */
    @Override
    public <V> void rightPush(String key, V value) {
        try {
            if (value != null) {
                redisTemplate
                        .opsForList()
                        .rightPush(key, JSON.toJSONString(value));
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
    }

    /**
     * 数据左插到缓存数据库key对应的列表中
     * @param key
     * @param value
     * @param <V>
     */
    @Override
    public <V> void leftPush(String key, V value) {
        try {
            if (value != null) {
                redisTemplate
                        .opsForList()
                        .leftPush(key, JSON.toJSONString(value));
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
    }

    /**
     * 根据key从对应列表左边弹出一条记录，并转为相应类型的对象
     * @param key
     * @param clazz
     * @param <V>
     * @return
     */
    @Override
    public <V> V leftPop(String key, Class<V> clazz) {
        String value = this.leftPop(key);
        V result = null;
        if (!StringUtils.isEmpty(value)) {
            result = JSON.parseObject(value,clazz);
        }
        return result;
    }

    /**
     * 根据key从对应列表右边弹出一条记录，并转为相应类型的对象
     * @param key
     * @param clazz
     * @param <V>
     * @return
     */
    @Override
    public <V> V rightPop(String key, Class<V> clazz) {
        String value = this.rightPop(key);
        V result = null;
        if (!StringUtils.isEmpty(value)) {
            result = JSON.parseObject(value,clazz);
        }
        return result;
    }

    /**
     * 根据key从对应列表左边弹出一条记录
     * @param key
     * @return
     */
    @Override
    public String leftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 根据key从对应列表右边弹出一条记录
     * @param key
     * @return
     */
    @Override
    public String rightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 数据缓存至redis的列表中(右插)
     * @param key
     * @param value
     * @return
     */
    @Override
    public <V> void addToList(String key, V value) {
        this.rightPush(key,value);
    }

    /**
     * 从key对应的列表中弹出一条记录,并转化为对应类型的对象(左出)
     * @param key
     * @param clazz
     * @return
     */
    @Override
    public <V> V popValueFromList(String key, Class<V> clazz) {
        return this.leftPop(key,clazz);
    }

    /**
     * 根据key查询列表中start~end区间内的记录
     * @param key
     * @param start
     * @param end
     * @return
     */
    @Override
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key,start,end);
    }
    /**
     * 根据key查询所有列表中的所有记录，并转化为对应类型
     * @param key
     * @param clazz 要转化成的类型
     * @param <V>
     * @return
     */
    @Override
    public <V> List<V> lRange(String key, Class<V> clazz, long start, long end) {
        List<String> strs=this.lRange(key,start,end);
        List<V> result = new ArrayList<V>();
        if(strs !=null && strs.size()>0){
            V temp = null;
            for (String str:strs) {
                temp = JSON.parseObject(str,clazz);
                result.add(temp);
            }
        }
        return result;
    }

    /**
     * 根据key查询所有列表中的所有记录，并转化为对应类型
     * @param key
     * @param clazz 要转化成的类型
     * @param <V>
     * @return
     */
    @Override
    public <V> List<V> getValuesFromList(String key, Class<V> clazz) {
        return this.lRange(key,clazz,0,-1);
    }

    /**
     * 根据key查询列表中start~end区间内的记录，并转化为对应类型
     * @param key
     * @param clazz 要转化成的类型
     * @param start 起始点
     * @param end 结束点
     * @param <V>
     * @return
     */
    @Override
    public <V> List<V> getValuesFromList(String key, Class<V> clazz, long start, long end) {
        return lRange(key,clazz,start,end);
    }

    /**
     * 根据key查询列表中start~end区间内的记录
     * @param key
     * @param start
     * @param end
     * @return
     */
    @Override
    public List<String> getValuesFromList(String key, long start, long end) {
        return lRange(key,start,end);
    }

    /**
     * 根据key获取对应列表的长度
     * @param key
     * @return
     */
    @Override
    public Long lLen(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 根据key获取对应列表的长度
     * @param key
     * @return
     */
    @Override
    public Long getSizeOfList(String key) {
        return this.lLen(key);
    }

    /**
     * 删除key
     * @param key
     */
    @Override
    public void delete(String key) {
        try{
            redisTemplate.delete(key);
        }catch (Exception e){
            logger.error("redis删除数据失败："+e.getMessage());
            throw new RuntimeException("redis删除数据失败:"+e.getMessage(),e);
        }
    }

    /**
     * 设置过期时间
     * @param key
     * @param timeout 时长
     * @param unit 时间单位
     * @return
     */
    @Override
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key,timeout,unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     * @param key
     * @return
     */
    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 返回 key 的剩余的过期时间,并转化为对应时间单位
     * @param key
     * @param unit 时间单位
     * @return
     */
    @Override
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key,unit);
    }

    /**
     * 移除 key 的过期时间，key 将持久保持
     * @param key
     * @return
     */
    @Override
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 获取所有key值(带默认前缀)
     * @return
     */
    @Override
    public Set<String> keys() {
        return redisTemplate.keys("*");
    }
    /**
     * 获取所有key值(带默认前缀)
     * @return
     */
    @Override
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    @Override
    public <V> void zAdd(String key, V value,double score) {
        try {
            if (value != null) {
                redisTemplate
                        .opsForZSet()
                        .add(key, JSON.toJSONString(value),score);
            }
        } catch (Exception e) {
            logger.error("数据缓存至redis失败:"+e.getMessage());
            throw new RuntimeException("数据缓存至redis失败:"+e.getMessage(),e);
        }
    }

    @Override
    public <V> void zAdd(String key, V value) {
        this.zAdd(key,value,0);
    }

    @Override
    public <V> Set<V> zRange(String key, Class<V> clazz, long start, long end) {
        Set<String> strs=this.zRange(key,start,end);
        Set<V> result = new LinkedHashSet<>();
        if(strs !=null && strs.size()>0){
            V temp = null;
            for (String str:strs) {
                temp = JSON.parseObject(str,clazz);
                result.add(temp);
            }
        }
        return result;
    }

    @Override
    public Set<String> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key,start,end);
    }

    @Override
    public Set<String> zRevrange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key,start,end);
    }

    @Override
    public <V> Set<V> zRevrange(String key, Class<V> clazz, long start, long end) {
        Set<String> strs=this.zRevrange(key,start,end);
        Set<V> result = new LinkedHashSet<>();
        if(strs !=null && strs.size()>0){
            V temp = null;
            for (String str:strs) {
                temp = JSON.parseObject(str,clazz);
                result.add(temp);
            }
        }
        return result;
    }

    @Override
    public <V> Set<V> zRangeAll(String key, Class<V> clazz) {
        return this.zRange(key,clazz,0,-1);
    }

    @Override
    public <V> Set<V> zRangeByScore(String key, Class<V> clazz, double min, double max) {
        Set<String> strs=this.zRangeByScore(key,min,max);
        Set<V> result = new LinkedHashSet<>();
        if(strs !=null && strs.size()>0){
            V temp = null;
            for (String str:strs) {
                temp = JSON.parseObject(str,clazz);
                result.add(temp);
            }
        }
        return result;
    }

    @Override
    public Set<String> zRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key,min,max);
    }

    @Override
    public long getSizeOfZSet(String key) {
        return redisTemplate.opsForZSet().size(key);
    }
}
